/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
#include <string.h>
#include <getopt.h>
#include <setjmp.h>
#include <unistd.h>

#include "smlib.h"
#include "smio.h"

CHAR *smquick_rcsid(void) {
    return("$Id: smquick.c,v 1.7 2008/02/19 13:15:50 bhepple Exp $");
}

extern int	errno;

static CHAR	*progname, pname[9];
static CHAR	rev_string[] = "$Revision: 1.7 $";
jmp_buf		NoMoreHeap;

int	quick_tag	= 0;
int	quick_max_field	= 30;
int	quick_labelsize = 10;
int	quick_tabular = 0;
int	quick_equal = 0;

#define TAG_RECORD_NAME "record-type"

/* stub */
void sm_error_exit ARG1(CHAR *, msg)
{
}

static CHAR *usage_msg[] = {
"Reads data files and designs screen layouts for them in SCRMGR format",
"-Fc : make c the field separator character for the data (default \'TAB\')",
"-tn : use field number n (first is 1) as the record-type. Default = 0 (none)",
"-ln : make labels n characters wide for each field (default = 10)",
"-fn : max field width n characters (default = 30)",
"-T  : create tabular screen instead of form", 
"-e  : makes all fields of equal width",
"Lines starting with \'#\' are treated as comments.",
NULL
};

void usage()
{
    CHAR **c;
    
    fprintf(stderr, "Revision %s of %s.\n", rev_string, pname);
    fprintf(stderr, "Usage: %s [-Fc] [-tn] [-ln] [-fn] [-T] [-e] [file]\n", pname);
    
    c = usage_msg;
    while (*c) {
        fputs(*c, stderr);
        fputc('\n', stderr);
        c++;
    }
    exit(2);
}

/* Stubs to be overidden if needed (at the moment, only if your application
   uses field triggers) */

f_ptr sm_get_field_trigger ARG1(SM_FIELD, field)
{
    return(NULL);
}

static int parse_line ARG4(int, 	separator, 
			   CHAR *, 	buf, 
			   int, 	line_num, 
			   SM_SCREEN *,	screenp)
{
    CHAR	*line = buf, fieldname[200], screenname[200], *sep;
    int		fieldno, row, col;
    SM_FIELD	*fieldp, field;
    SM_SCREEN	screen;
    
    /* remove leading whitespace */
    while (line && isspace(*line))
        line++;
    
    /* ignore commments */
    if (!*line || *line == '#')
        return(0);
    
    /* On tagged files, find the record-type */
    if (quick_tag) {
	fieldno = 0;
	while (*line && (fieldno != quick_tag)) {
	    /* get the next keyword */
	    sep = sm_parse(line, 0, separator);
	    line = sm_trim(line);
	    fieldno++;
	    strcpy(screenname, line);
	    if (sep)
		*sep = separator;
	    
	    line = sm_nextfield(separator, line, sep, 0);
	}
	
	if (fieldno != quick_tag) {
	    sm_line_error("No tag - line ignored", buf, line_num);
	    return(0);
	} else {
	    if ((screen = sm_scr_open(sm_user_screens(), screenname)) == NULL) {
		screenp = sm_create_scr(sm_user_screens());
		screen = *screenp;
		sm_rename_scr(screen, screenname);
	    }
	}
    } else
	screen = *screenp;

    /* set up row & column */
    fieldp = sm_first_fld(screen); 
    row = 0;
    col = quick_labelsize;
    while (fieldp && *fieldp) {
	row = ((*fieldp)->row > row)? (*fieldp)->row: row;
	fieldp = sm_next_fld(fieldp);
    }

    /* Create the fields */
    fieldno = 0;
    line = buf;
    fieldp = sm_first_fld(screen); 
    
    while (*line) {
        /* Get the next data string */
        sep = sm_parse(line, 0, separator);
        line = sm_trim(line);
        fieldno++;
	
        /* Create a field, if necessary */
        if ((fieldp == NULL) || (*fieldp == NULL)) {
            fieldp = sm_create_fld(screen);
	    field = *fieldp;

            sm_set_fld_type(field, 0);
            if (fieldno == quick_tag) {
                /* Create a hidden, protected field for the tag */
                sm_rename_fld(field, TAG_RECORD_NAME);
                sm_set_fld_attr(field, 1, 1, 1);
            } else {
                sm_set_fld_attr(field, 1, 0, 0);
                sm_set_fld_len(field, 1);
                sprintf(fieldname, "Field%d", fieldno);
                sm_rename_fld(field, fieldname);
                
                (row)++;
		if (row > 22) {
		    row = 1;
		    col = quick_max_field + 2 * quick_labelsize + 1;
		}
                sm_set_fld_pos(field, row, col);
            }
        } else
	    field = *fieldp;
	
        if (fieldno != quick_tag) {
	    if (quick_equal)
		sm_set_fld_len(field, quick_max_field);
	    else if (sm_get_fld_len(field) < strlen(line))
		sm_set_fld_len(field, 
			       (strlen(line) < quick_max_field)?
			       strlen(line):
			       quick_max_field);
        }
	
        fieldp = sm_next_fld(fieldp);
        line = sm_nextfield(separator, line, sep, 0);
        if (sep)
            *sep = separator;
    }
    return(0);
}

void make_tabular_screen ARG1(SM_SCREEN, screen)
{
    if (!quick_tag) {
	int		num_fields = 0;
	int		total_width = 0;
	int		row, col;
	SM_FIELD	*fieldp;
	int		loops = 0;
	
	/* count the fields */
	fieldp = sm_first_fld(screen);
	while (fieldp && *fieldp) {
	    num_fields++;
	    total_width += sm_get_fld_len(*fieldp);
	    fieldp = sm_next_fld(fieldp);
	}
	
#define MAX_FIELDS	40
#define MAX_COL		80

	if (num_fields > MAX_FIELDS) {
	    fprintf(stderr, "Too many fields (%d > %d) in this file for a Tabular screen.\n", num_fields, MAX_FIELDS);
	    return;
	}

	if (!quick_equal)
	    while (total_width + num_fields > MAX_COL) {
		/* we need to reduce field widths */
		float	factor;
		int    	new_len;
		
		factor = ((float) (MAX_COL - num_fields)) / 
		    ((float) (total_width + num_fields));
		if (++loops > 100) { /* Just in case ... */
		    fprintf(stderr, "Can't fit the fields on one line.\n");
		    return;
		}
		
		fieldp = sm_first_fld(screen);
		while (fieldp && *fieldp && (total_width + num_fields > MAX_COL)) {
		    new_len = sm_get_fld_len(*fieldp) * factor + 1; /* rounds up */
		    if (new_len < 1)
			new_len = 1;
		    total_width -= (sm_get_fld_len(*fieldp) - new_len);
		    sm_set_fld_len(*fieldp, new_len);
		    fieldp = sm_next_fld(fieldp);
		}
	    }
	
	/* Now reassign row & col addresses */
	row = 1;
	col = 0;
	fieldp = sm_first_fld(screen);
	while (fieldp && *fieldp) {
	    if (quick_equal)
		sm_set_fld_len(*fieldp, (MAX_COL - num_fields) / num_fields);
	    sm_set_fld_pos(*fieldp, row, col);
	    col += (sm_get_fld_len(*fieldp) + 1);
	    fieldp = sm_next_fld(fieldp);
	}
	
	/* set screen parameters ... */
	screen->scrolling = 1;
    }
}

static void build_field_ordering ARG1(SM_SCREEN, screen)
{
    SM_FIELD	*fp;

    for (fp = sm_first_fld(screen);
	 fp && *fp;
	 fp = sm_next_fld(fp)) {
	CHAR	*p = xmalloc((INDEX) (strlen((*fp)->field_name) + 1));

	strcpy(p, (*fp)->field_name);
	screen->data_field = addToArray(screen->data_field, p);
    }
}

int process ARG2(FILE *, f, CHAR *, filename)
{
    int		errors = 0;
    CHAR	*buf = alloca(SM_MAXREC), *pos, *end;
    int		line_num = 0;
    SM_SCREEN	*screenp;
    static int	screen_num = 0;

    if (!(*filename) && isatty(fileno(f)))
        fprintf(stderr, "Terminate input with ^D\n");
    
    /* Open the screen for non-tagged files */
    if (!quick_tag) {
        screenp = sm_create_scr(sm_user_screens());
	
        sprintf(buf, "Screen%d", ++screen_num);
        sm_rename_scr(*screenp, buf);
    } else
        screenp = NULL;

    pos = &buf[0];
    while (fgets(pos, SM_MAXREC - (pos - buf), f) != NULL) {
        
        line_num++;
        /* Bark at long lines - but try to continue */
        if (strlen(pos) >= SM_MAXREC - (pos - buf) - 1)
            sm_line_error("Line too long", buf, line_num);
        
        if ((end = strchr(pos, '\n')) != NULL)
            *end = 0;
        if ((end = strchr(pos, '\r')) != NULL)
            *end = 0;
        if (end == NULL)
            end = SM_LASTCHAR(pos);
        *(end + 2) = 0; /* extra 0 */
        if (*end == '\\') {
            *end = 0;
            pos = end;
        } else {
            if (parse_line(sm_user_screens()->separator, 
                    buf, 
                    line_num, 
                    screenp))
                return(++errors);
            pos = buf;
        }
    }

    if (quick_tag) /* just use the first screen */
        screenp = sm_first_scr(sm_user_screens());

    if (quick_tabular)
        make_tabular_screen(screenp? *screenp: NULL);

    build_field_ordering(screenp? *screenp: NULL);

    if (filename && *filename)
        SM_ASSIGN((*screenp)->filename, filename);

    return(errors);		
}

int main ARG2(int, argc, CHAR **, argv)
{
    FILE	*f;
    CHAR	*sep_arg = NULL;
    int		c,
    		errors = 0;

    progname = argv[0];
    fname(pname, progname);
    
    while ((c = getopt(argc, argv, "heTF:f:l:t:?")) != EOF)
        switch (c) {
        case 'F':
	    sep_arg = optarg;
            break;
        case 'f':
            quick_max_field = atoi(optarg);
            break;
        case 'l':
            quick_labelsize = atoi(optarg);
            break;
	case 'T':
	    quick_tabular = 1;
	    break;
        case 't':
            quick_tag = atoi(optarg);
            break;
	case 'e':
	    quick_equal = 1;
	    break;
        case '?':
        default:
            usage();
            break;
        }

    if (setjmp(NoMoreHeap)) {
	fprintf(stderr, "Ran out of heap!\n");
	exit(1);
    }

    sm_init(0, argc, argv);
    smio_term();

    if (sep_arg)
	sm_set_separator(sm_user_screens(), *sep_arg);
    else
	sm_set_separator(sm_user_screens(), '\t');

    if (quick_tag)
	SM_ASSIGN(sm_user_screens()->tagname, TAG_RECORD_NAME);

    if (optind >= argc)
        errors += process(stdin, "");
    else
        for (; optind < argc; optind++)
            if (strcmp(argv[optind], "-") == 0)
                errors += process(stdin, "");
            else {
		if ((f = fopen(argv[optind], "r")) == NULL) {
		    fprintf(stderr,	
			    "%s: Can\'t open \'%s\'. <%s>\n",
                        pname, argv[optind], strerror(errno));
		} else {
		    errors += process(f, argv[optind]);
		    fclose(f);
		}
            }

    if (!errors)
	sm_fwrite(sm_user_screens(), stdout);

    exit(errors);
}


/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
